/*  SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************

  Audacity: A Digital Audio Editor

  CloudSyncService.h

  Dmitry Vedenko

**********************************************************************/
#pragma once

#include <atomic>
#include <functional>
#include <future>
#include <memory>
#include <string>
#include <string_view>
#include <variant>
#include <vector>

#include "NetworkUtils.h"

#include "concurrency/CancellationContext.h"

class AudacityProject;

namespace audacity::cloud::audiocom {
namespace sync {
class LocalProjectSnapshot;
class PaginatedProjectsResponse;
class ProjectInfo;
class SnapshotInfo;
class RemoteProjectSnapshot;

struct ProjectSyncResult final
{
    enum class StatusCode
    {
        Succeeded,
        Blocked,
        Failed,
    };

    StatusCode Status {};
    ResponseResult Result;
    std::string ProjectPath;
    TransferStats Stats;
}; // struct ProjectSyncResult

using ProgressCallback = std::function<bool (double)>;

using GetProjectsResult
    =std::variant<sync::PaginatedProjectsResponse, ResponseResult>;

using GetHeadSnapshotIDResult = std::variant<std::string, ResponseResult>;
} // namespace sync

//! CloudSyncService is responsible for saving and loading projects from the
//! cloud
class CLOUD_AUDIOCOM_API CloudSyncService final
{
    CloudSyncService()  = default;
    ~CloudSyncService() = default;

    CloudSyncService(const CloudSyncService&)            = delete;
    CloudSyncService(CloudSyncService&&)                 = delete;
    CloudSyncService& operator=(const CloudSyncService&) = delete;
    CloudSyncService& operator=(CloudSyncService&&)      = delete;

public:
    static CloudSyncService& Get();

    using SyncPromise = std::promise<sync::ProjectSyncResult>;
    using SyncFuture  = std::future<sync::ProjectSyncResult>;

    using GetProjectsPromise = std::promise<sync::GetProjectsResult>;
    using GetProjectsFuture  = std::future<sync::GetProjectsResult>;

    using GetHeadSnapshotIDPromise = std::promise<sync::GetHeadSnapshotIDResult>;
    using GetHeadSnapshotIDFuture  = std::future<sync::GetHeadSnapshotIDResult>;

    enum class SyncMode
    {
        Normal,
        ForceOverwrite,
        ForceNew
    };

    //! Retrieve the list of projects from the cloud
    GetProjectsFuture GetProjects(
        concurrency::CancellationContextPtr context, int page, int pageSize, std::string_view searchString);

    //! Open the project from the cloud. This operation is asynchronous.
    [[nodiscard]] SyncFuture OpenFromCloud(
        std::string projectId, std::string snapshotId, SyncMode mode, sync::ProgressCallback callback);

    [[nodiscard]] SyncFuture SyncProject(
        AudacityProject& project, const std::string& path, bool forceSync, sync::ProgressCallback callback);

    static bool IsCloudProject(const std::string& path);

    enum class ProjectState
    {
        NotAvaliable,
        PendingSync,
        FullySynced
    };
    static ProjectState GetProjectState(const std::string& projectId);

    GetHeadSnapshotIDFuture GetHeadSnapshotID(std::string_view projectId);

private:
    void FailSync(ResponseResult responseResult);

    void CompleteSync(std::string path);

    void CompleteSync(sync::ProjectSyncResult result);

    void SyncCloudSnapshot(
        const sync::ProjectInfo& projectInfo, const sync::SnapshotInfo& snapshotInfo, SyncMode mode);

    void UpdateDowloadProgress(double downloadProgress);

    void
    ReportUploadStats(std::string_view projectId, const TransferStats& stats);

    std::vector<std::shared_ptr<sync::LocalProjectSnapshot>> mLocalSnapshots;
    std::shared_ptr<sync::RemoteProjectSnapshot> mRemoteSnapshot;

    SyncPromise mSyncPromise;
    sync::ProgressCallback mProgressCallback;

    std::atomic<double> mDownloadProgress { 0.0 };
    std::atomic<bool> mProgressUpdateQueued { false };

    std::atomic<bool> mSyncInProcess { false };
};
} // namespace audacity::cloud::audiocom
